<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>9职责链模式</title>
  </head>
  <body>
    职责链模式:：使多个对象都有机会处理请求，从而避免请求的发送者和接收者之间
    的耦合关系，将这些对象连成一条链，并沿着这条链传递该请求，直到有一个对象处理它为止。 从而避免if else 多层嵌套
    (单层的if else 用策略模式 多层if else 用职责链模式)
    <script>
      // orderType、pay、stock 这 3 个字段当作参数传递给 500 元订单函数，如果该函数不
      // 符合处理条件，则把这个请求传递给后面的 200 元订单函数，如果 200 元订单函数依然不能处理
      // 该请求，则继续传递请求给普通购买函数，代码如下

      // 500 元订单
      const _order500 = function (orderType, pay, stock) {
        if (orderType === 1 && pay === true) {
          console.log('500 元定金预购, 得到 100 优惠券');
        } else {
          _order200(orderType, pay, stock); // 将请求传递给 200 元订单
        }
      };
      // 200 元订单
      const _order200 = function (orderType, pay, stock) {
        if (orderType === 2 && pay === true) {
          console.log('200 元定金预购, 得到 50 优惠券');
        } else {
          _orderNormal(orderType, pay, stock); // 将请求传递给普通订单
        }
      };
      // 普通购买订单
      const _orderNormal = function (orderType, pay, stock) {
        if (stock > 0) {
          console.log('普通购买, 无优惠券');
        } else {
          console.log('手机库存不足');
        }
      };

      /* 更灵活的职责链模式  目标是让链中的各个节点可灵活拆分和重组。 */

      // 首先需要改写一下分别表示 3 种购买模式的节点函数，我们约定，如果某个节点不能处理请
      // 求，则返回一个特定的字符串 'nextSuccessor'来表示该请求需要继续往后面传递：
      const order500 = function (orderType, pay, stock) {
        if (orderType === 1 && pay === true) {
          console.log('500 元定金预购，得到 100 优惠券');
        } else {
          return 'nextSuccessor'; // 我不知道下一个节点是谁，反正把请求往后面传递
        }
      };
      const order200 = function (orderType, pay, stock) {
        if (orderType === 2 && pay === true) {
          console.log('200 元定金预购，得到 50 优惠券');
        } else {
          return 'nextSuccessor'; // 我不知道下一个节点是谁，反正把请求往后面传递
        }
      };
      const orderNormal = function (orderType, pay, stock) {
        if (stock > 0) {
          console.log('普通购买，无优惠券');
        } else {
          console.log('手机库存不足');
        }
      };

      // 包装职责链节点
      class Chain {
        // 下一次节点
        nextChain = null;
        // 当前节点
        curChain = null;
        // 初始化当前节点
        constructor(fn) {
          this.curChain = fn;
        }
        // 设置下一次节点
        setNextSuccessor(nextChain) {
          return (this.nextChain = nextChain);
        }
        // 处理节点
        passRequest(...args) {
          const ret = this.curChain.apply(this, args);
          if (ret === 'nextSuccessor') {
            return this.nextChain?.passRequest.apply(this.nextChain, args);
          }
          return ret;
        }
        // 处理异步请求的节点
        next(...args) {
          return this.nextChain && this.nextChain.passRequest.apply(this.nextChain, args);
        }
      }

      /**
       * 同步职责链
       */
      // // 现在我们把 3 个订单函数分别包装成职责链的节点：
      // const chainOrder500 = new Chain(order500);
      // const chainOrder200 = new Chain(order200);
      // const chainOrderNormal = new Chain(orderNormal);

      // // 然后指定节点在职责链中的顺序：
      // chainOrder500.setNextSuccessor(chainOrder200);
      // chainOrder200.setNextSuccessor(chainOrderNormal);
      // // 最后把请求传递给第一个节点：
      // chainOrder500.passRequest(1, true, 500); // 输出：500 元定金预购，得到 100 优惠券
      // chainOrder500.passRequest(2, true, 500); // 输出：200 元定金预购，得到 50 优惠券
      // chainOrder500.passRequest(3, true, 500); // 输出：普通购买，无优惠券
      // chainOrder500.passRequest(1, false, 0); // 输出：手机库存不足

      /**
       * 异步职责链
       */
      const fn1 = new Chain(function () {
        console.log(1);
        return 'nextSuccessor';
      });
      const fn2 = new Chain(function () {
        console.log(2);
        const _this = this;
        setTimeout(function () {
          _this.next();
        }, 1000);
      });

      const fn3 = new Chain(function () {
        console.log(3);
      });
      fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);

      fn1.passRequest();

      // 现在我们得到了一个特殊的链条，请求在链中的节点里传递，但节点有权利决定什么时候把
      // 请求交给下一个节点。可以想象，异步的职责链加上命令模式（把 ajax 请求封装成命令对象，详
      // 情请参考第 9 章），我们可以很方便地创建一个异步 ajax 队列库。

      /**
       * AOP职责链模式
       */
      Function.prototype._after = function (fn) {
        const self = this;
        return function () {
          const ret = self.apply(this, arguments);
          if (ret === 'nextSuccessor') {
            return fn.apply(this, arguments);
          }
          return ret;
        };
      };
      const order = order500yuan._after(order200yuan)._after(orderNormal);
      order(1, true, 500); // 输出：500 元定金预购，得到 100 优惠券
      order(2, true, 500); // 输出：200 元定金预购，得到 50 优惠券
      order(1, false, 500); // 输出：普通购买，无优惠券

      // 在 JavaScript 开发中，职责链模式是最容易被忽视的模式之一。实际上只要运用得当，职责
      // 链模式可以很好地帮助我们管理代码，降低发起请求的对象和处理请求的对象之间的耦合性。职
      // 责链中的节点数量和顺序是可以自由变化的，我们可以在运行时决定链中包含哪些节点。
      // 无论是作用域链、原型链，还是 DOM 节点中的事件冒泡，我们都能从中找到职责链模式的
      // 影子。职责链模式还可以和组合模式结合在一起，用来连接部件和父部件，或是提高组合对象的
      // 效率。学会使用职责链模式，相信在以后的代码编写中，将会对你大有裨益
    </script>
  </body>
</html>
